import bcrypt from 'bcryptjs';
import jwt, { SignOptions, JwtPayload } from 'jsonwebtoken';
import config from '../config/env';

const SALT_ROUNDS = 10;

function parseDuration(duration: string): number {
  const match = duration.match(/^(\d+)([hmd])$/);
  if (!match) {
    return 24 * 60 * 60; // Default to 24 hours in seconds
  }

  const value = parseInt(match[1], 10);
  const unit = match[2];

  switch (unit) {
    case 'h':
      return value * 60 * 60;
    case 'm':
      return value * 60;
    case 'd':
      return value * 24 * 60 * 60;
    default:
      return 24 * 60 * 60;
  }
}

export async function hashPassword(password: string): Promise<string> {
  return bcrypt.hash(password, SALT_ROUNDS);
}

export async function comparePasswords(plainPassword: string, hashedPassword: string): Promise<boolean> {
  return bcrypt.compare(plainPassword, hashedPassword);
}

type TokenOptions = Partial<Omit<SignOptions, 'expiresIn'>> & {
  expiresIn?: string | number;
};

export function generateToken(
  payload: Record<string, any>, 
  options: TokenOptions = {}
): string {
  // Convert duration string to seconds if it's a string
  const expiresIn = typeof options.expiresIn === 'string' 
    ? parseDuration(options.expiresIn)
    : options.expiresIn ?? parseDuration(config.jwt.expiresIn);

  const signOptions: SignOptions = {
    ...options,
    expiresIn,
  };

  return jwt.sign(payload, config.jwt.secret, signOptions);
}

export function verifyToken<T extends JwtPayload>(token: string): T {
  return jwt.verify(token, config.jwt.secret) as T;
}

export function generateRandomString(length = 32): string {
  return Array.from(
    { length }, 
    () => Math.random().toString(36).charAt(2)
  ).join('');
}

export function generatePasswordResetToken(): string {
  return generateRandomString(32);
}

export function hashData(data: string): string {
  return bcrypt.hashSync(data, SALT_ROUNDS);
}

// For testing purposes
export const __testing = {
  SALT_ROUNDS,
  parseDuration,
};
